package stcpscheculer;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

public class Population{

    int size;
    public int Buses;
    public int Drivers;
    public int TimeSlot; // in hours
    public int chromosomeLength;
    public Chromosome[] Population;
    public Chromosome bestSolution;
    public float[] Psel; // probabilities of being selected
    public float FA; // sum of all fa of the population

    /**
     *
     * @param int size
     * @param int buses
     * @param int drivers
     * @param int timeseg
     */
    public Population() {
        this.size = Configuration.getPopulationSize();
	this.Buses = Configuration.getBuses();
	this.Drivers = Configuration.getDrivers();
	this.TimeSlot = Configuration.getTimeSlot();
        this.chromosomeLength = Configuration.getChromosomeLength();
        FA = 0;
        Psel = new float[size];
        Population = new Chromosome[size];
        bestSolution = new Chromosome();
    }
    
    /**
     * Copy object of type Population
     */
    public Population(Population toCopy){
        this.size = toCopy.size;
        this.Buses = toCopy.Buses;
        this.Drivers = toCopy.Drivers;
        this.TimeSlot = toCopy.TimeSlot; // in hours
        this.chromosomeLength = toCopy.chromosomeLength;
        this.Population = new Chromosome[size];
        //this.Population = toCopy.Population;
        for(int i=0;i<this.size;i++){
            this.Population[i] = new Chromosome(toCopy.Population[i]);
        }
        this.bestSolution = toCopy.bestSolution;
        this.Psel = toCopy.Psel; // probabilities of being selected
        this.FA = toCopy.FA; // sum of all fa of the population
        
    }

    public Population getPopulation() {
        return this;
    }

    public Chromosome[] getChromosomes() {
        return Population;
    }

    public Chromosome getChromosomeAt(int i) {
        return Population[i];
    }

    /**
     * Evaluate population by running evaluation function for each individual
     *
     */
    public void evaluatePopulation() {
        FA = 0;
        for(Chromosome chr: Population){
            FA += chr.fa();
        }
    }

    /**
     * Calculates for each chromosome probability to be selected 
     * for the production of next generation
     * 
     */
    public void calcPsel() {
        //System.out.println("Psel: ");
        for(int i=0; i<size; i++){
            Psel[i] = Population[i].getAdaptation()/Math.abs(FA);
          //  System.out.println(i+": "+Psel[i]);
        }
    }
    
    /**
     * Runs roulete to select chromosomes that will be used 
     * to produce a new generation
     * 
     * @return int[] - an array of selected chromosomes ids 
     */
    public int[] selectionRoulete(){
        int[] selected;
        selected = new int[size - 3];
        float[] Ps;
        Ps = new float[size]; // array of borders of the roulete
        //System.out.println("Selection Roulette: ");
        Ps[0]= Psel[0]; 
        //System.out.println(Ps[0]);
        for(int i=1;i<size;i++){
            if(Psel[i] == 0) 
                Ps[i] = Ps[i-1] + 0.1f;
            else 
                Ps[i] = Ps[i-1] + Psel[i];
          //  System.out.println(Ps[i]);
        }
        int max = (int)(Ps[size-1] * 1000.0f);
        Random generator = new Random();
        System.out.print("Selected (max = " + max/1000.0f + "): ");
        for(int i=0;i<size-3;i++){
            float x = (float)generator.nextInt(max)/1000.0f;
            System.out.print(" [" + x + "] ");
            // find sector on roulete 
            for(int j=0;j<size;j++){
                if(x<=Ps[j]){
                    selected[i] = j;
                    break;
                }
            }
            System.out.print(selected[i] + ", ");
        }
        System.out.println("");
        return selected;
    } 

    /**
     * Runs a roulet to choose chromosomes that will be crossed
     * 
     * @return int[] - an array of chromosomes' ids to be crossed
     */
    public int[] crossRoulete(){
        ArrayList<Integer> toCross = new ArrayList<Integer>();
        Random generator = new Random();
        for(int i=3;i<size;i++){
            float x = (float)generator.nextInt(1001)/1000.0f;
            if(x<=Configuration.getPcross()){
                toCross.add(i);
            }
        }
        int[] array = new int[toCross.size()];
        int i=0;
        System.out.print("To Cross: ");
        for(Integer s: toCross){
            array[i++] = s;
            System.out.print(s + ", ");
        }
        System.out.println("");
        return array;
    } 
    
    /**
     * Apply mutation to each chromosome in the population
     * 
     */
    public void mutate() {
        //System.out.print("Mutation at: ");
        // starts from 4th element to ensure that elite remains unchanged
        for(int i=3; i<size; i++){
            Population[i].mutate();
        }
    }
    
    /**
     * Creates initial population
     * 
     */
    public void initPopulation() {
        for (int i = 0; i < size; i++) {
            // create new chromosome
            Population[i] = new Chromosome();
            Population[i].init();
        }
    }
    
    /**
     * Compares solutions within a population
     * 
     * @return Chromosome that has the best value of fa()
     */
    public Chromosome bestSolution(){
        Chromosome chr = new Chromosome();
        float fa = 0;
        for(int i=0;i<size;i++){
            if(Population[i].getAdaptation()>fa){
                fa=Population[i].getAdaptation();
                chr=Population[i];
            }
        }
        bestSolution = chr;
        //System.out.print("Best soulution: ");
        //bestSolution.PrintSolution();
        return bestSolution;
    }

    public Chromosome getBestSolution() {
        return bestSolution;
    }
    
    public void PrintPopulation(){
        for(int i=0;i<size;i++){
            System.out.print("Solution " + i + ":  ");
            Population[i].PrintSolution();
        }
    }
    
    
    public void SortPopulation(){
        Arrays.sort(this.Population, Chromosome.descFA);
    }
}
